// Copyright (C) 2011, 2012 by ma-Go-me. All rights reserved.

package main
import (
    "net"
    "os"
	"syscall"
	"io"
    "fmt"
)

func debug(msg string) {
	fmt.Fprintf(os.Stdout, msg + "\n")
}

func hereIAm(remoteIP string) {
	debug("hereIAM remoteIP = " + remoteIP)
	rAddr, _:= net.ResolveUDPAddr("", remoteIP + ":1979")
	lAddr, _:= net.ResolveUDPAddr("","")
	udpConn, _ := net.DialUDP("udp4", lAddr, rAddr)
	udpConn.Write([]byte("Hello! I'm Yoshiki's Chat Server (20110917-01)"))
	udpConn.Close()
}

func main() {
	debug("main")
    psock, e := net.Listen("tcp", ":1978")
    if e != nil {
        return
    }

    // message queue
    queue := make(chan string)
    // channel map
    chan_for := make(map [net.Conn] chan string)

	go func() {
		debug("UDP listening ...")
		udpAddr, _:= net.ResolveUDPAddr("",":1978")
		udpConn, e := net.ListenUDP("udp", udpAddr)
		if e != nil {
			debug("UPD ListendUDP error")
			return
		}
        buffer := make([]byte, 2048)
		for {
			l, remoteUDPAddr, _ := udpConn.ReadFromUDP(buffer)
            debug("Message from " + remoteUDPAddr.String() + ": " + string(buffer[0:l]))
			hereIAm(remoteUDPAddr.IP.String())
		}
	}()

    // deliver thread
    go func() {
        for {
            msg := <-queue
			debug(msg)
            for _, q := range chan_for {
                q <- msg
            }
        }
    }()

    for {
		debug("Waiting accept")
        conn, e := psock.Accept()
		debug("Accepted")
        if e != nil {
            return
        }

        // reader thread
        go func() {
			debug("goroutine 2")
            defer func () {
                conn.Close()
                delete(chan_for,conn)
            }()
            buffer := make([]byte, 1024)

            for {
                l, e := conn.Read(buffer)
                switch {
                case e == nil:
                    queue <- string(buffer[0:l])
                case e == io.EOF:
                    return
                case e != syscall.EAGAIN:
                    return
                }
            }
        }()

        w_ch := make(chan string)
        chan_for[conn] = w_ch

        // writer thread
        go func() {
            for {
                msg := <-w_ch
                _, err := conn.Write([]byte(msg))
                if err != nil {
                    break
                }
            }
        }()
    }
}
